package com.philosophy.web.service.impl;

import com.philosophy.web.domain.generate.RoutingDB;
import com.philosophy.web.domain.generate.UrlMapping;
import com.philosophy.web.event.VisitEvent;
import com.philosophy.web.filter.RedirectFilter;
import com.philosophy.web.mapper.UrlMappingMapper;
import com.philosophy.web.service.VisitService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.ApplicationEventPublisherAware;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.lang.NonNull;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.util.Optional;

import static com.philosophy.constant.RedisConstant.COUNT_KEY_FORMAT;

/**
 * @author philosophy
 */
@Service
@Slf4j
public class VisitServiceImpl implements VisitService, ApplicationEventPublisherAware {

    private ApplicationEventPublisher applicationEventPublisher;
    @Resource
    private RedisTemplate<String, UrlMapping> redisTemplate;
    @Resource
    private RedirectFilter redirectFilter;
    @Resource
    private UrlMappingMapper urlMappingMapper;

    @Override
    public void setApplicationEventPublisher(@NonNull ApplicationEventPublisher applicationEventPublisher) {
        this.applicationEventPublisher = applicationEventPublisher;
    }

    @Override
    public String getOriginUrl(String shortUrl) {
        UrlMapping urlMapping = getUrlMapping(shortUrl);
        this.applicationEventPublisher.publishEvent(new VisitEvent(urlMapping));
        log.info("=======统计url访问事件发成功!");
        return urlMapping.getOriginUrl();
    }

    /**
     * 获取urlMapping
     *
     * @param shortUrl shortUrl
     * @return UrlMapping
     */
    private UrlMapping getUrlMapping(String shortUrl) {
        //使用布隆过滤器过滤无效请求
        if (!redirectFilter.isExistShortUrl(shortUrl)) {
            throw new RuntimeException("短链接不存在:" + shortUrl);
        }
        int databaseNum = RoutingDB.getDatabaseNum(shortUrl);
        String key = String.format(COUNT_KEY_FORMAT, databaseNum);
        UrlMapping urlMapping = (UrlMapping) redisTemplate.opsForHash().get(key, shortUrl);
        urlMapping = Optional.ofNullable(urlMapping).orElseGet(() -> urlMappingMapper.getByShortUrl(shortUrl));
        return isExpireUrlMapping(urlMapping, key, shortUrl);
    }

    /**
     * 判断urlMapping是否过期,过期则删除过期的数据，并抛出提示异常信息
     *
     * @param urlMapping urlMapping
     * @return UrlMapping
     */
    private UrlMapping isExpireUrlMapping(UrlMapping urlMapping, String key, String shortUrl) {
        LocalDateTime now = LocalDateTime.now();
        boolean flag = Optional.ofNullable(urlMapping).map(UrlMapping::getEndTimestamp).orElse(now).isAfter(now);
        if (!flag) {
            redisTemplate.opsForHash().delete(key, shortUrl);
            throw new RuntimeException("url 映射已过期！");
        }
        return urlMapping;
    }


}
